/**
 * @(#)ReserveInstallServiceImpl.java 2017年9月25日
 * Copyright(C) 2017 ZHEXIN IT CO.,LTD. All rights reserved.
 */
package org.jsbd.boss.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.jsbd.boss.common.redis.RedisClient;
import org.jsbd.boss.dao.IAddReserveRecordDAO;
import org.jsbd.boss.dao.IChannelApkConfigDAO;
import org.jsbd.boss.domian.channel.AddReserveRecordVo;
import org.jsbd.boss.domian.channel.ApkPRIConfig;
import org.jsbd.boss.domian.channel.QueryUserClientVo;
import org.jsbd.boss.domian.channel.ReserveUserVo;
import org.jsbd.boss.domian.channel.ThirdUserQueryVo;
import org.jsbd.boss.domian.channel.UserClientInfo;
import org.jsbd.boss.enums.ApkBaseEnum;
import org.jsbd.boss.enums.CarrierEnum;
import org.jsbd.boss.enums.DataCheckStatusEnum;
import org.jsbd.boss.enums.HistoryUserTableEnum;
import org.jsbd.boss.service.ApkManager;
import org.jsbd.boss.service.ClientManager;
import org.jsbd.boss.service.IReserveInstallService;
import org.jsbd.boss.service.IUserClientInfoService;
import org.jsbd.boss.util.CacheKey;
import org.jsbd.boss.util.DateUtil;
import org.jsbd.boss.util.ProvinceUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * 预留安装用户
 *
 * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
 * @version 1.0.0
 */
@Service("reserveInstallService")
public class ReserveInstallServiceImpl implements IReserveInstallService {
    public static final Logger LOGGER = LoggerFactory.getLogger(ReserveInstallServiceImpl.class);

    @Resource
    private IUserClientInfoService userClientService;
    @Resource
    private ClientManager clientManager;

    @Resource
    private IChannelApkConfigDAO channelApkConfigDAO;
    @Resource
    private RedisClient redisClient;
    @Resource
    private IAddReserveRecordDAO addReserveRecordDAO;

    @Resource
    private CacheParamManager cacheParamManager;

    private int MAXSIZE = 20;

    private List<UserClientInfo> selectUserList(ThirdUserQueryVo queryVo) {
        QueryUserClientVo userVo = new QueryUserClientVo();
        userVo.setId((long) queryVo.getStart());
        userVo.setLength(queryVo.getLength());
        userVo.setProvince(queryVo.getProvinceId());
        userVo.setCarrier(queryVo.getCarrier());
        userVo.setTableName(HistoryUserTableEnum.HISTORY.getCode());
        List<UserClientInfo> clientList = userClientService.selectClientList(userVo);
        if (clientList != null && clientList.size() > 0) {
            Iterator<UserClientInfo> userIterator = clientList.iterator();
            while (userIterator.hasNext()) {
                UserClientInfo userInfo = userIterator.next();
                userInfo.setProvince(queryVo.getProvinceId());
                if (clientManager.checkClient(userInfo) == DataCheckStatusEnum.FAIL) {
                    userIterator.remove();
                }
            }
            return clientList;
        } else {
            return new ArrayList<UserClientInfo>();
        }

    }

    @Override
    public void initInstallUserData() {
        // 1、查询有预安装的apk
        // 2、查询已经执行的新增量
        // 3、选择一定比例的用户插入预留记录表
        Map<String, ApkPRIConfig> apkConfigMap = initApkOfProvince();
        String[] apks = apkConfigMap.keySet().toArray(new String[0]);
        for (String apk : apks) {
            String lockKey = CacheKey.R_I_LOCK + apk;
            if (redisClient.evalSetNx(lockKey, 120)) {
                ApkPRIConfig apkConfig = apkConfigMap.get(apk);
                Integer installRate = apkConfig.getInstallRate();
                String apkRecordKey = CacheKey.A_TASK + DateUtil.getDaysOfTime() + apk;
                String execTotal = redisClient.evalHget(apkRecordKey, "exectotal");
                if (StringUtils.isNotBlank(execTotal)) {
                    int needExecTotal = Integer.parseInt(execTotal) * installRate / 100;
                    int reserveTotal = 0;
                    String reserveExecTotal = redisClient.evalHget(apkRecordKey, "reserveexectotal");
                    if (StringUtils.isNotBlank(reserveExecTotal)) {
                        reserveTotal = Integer.parseInt(reserveExecTotal);
                    }
                    int needTotal = needExecTotal - reserveTotal;
                    if (needTotal > 0) {
                        initReserveUserList(apkConfig, needTotal);
                    }
                }
                redisClient.remove(lockKey);
            }

        }
    }

    /**
     * initApkOfProvince:计算apk对应的省份信息
     *
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private Map<String, ApkPRIConfig> initApkOfProvince() {
        // 1、省份下的apk信息
        ApkPRIConfig config = new ApkPRIConfig();
        config.setInstallRate(1);
        config.setIsDelete("0");
        List<ApkPRIConfig> configList = channelApkConfigDAO.selectApkListOfProvince(config);
        Map<String, ApkPRIConfig> apkConfigMap = new HashMap<String, ApkPRIConfig>();
        for (ApkPRIConfig temp : configList) {
            String apk = temp.getMark();
            ApkPRIConfig apkConfig = apkConfigMap.get(apk);
            if (apkConfig == null) {
                apkConfig = temp;
            }
            Set<Integer> provinceSet = apkConfig.getProvinceIds();
            if (provinceSet == null) {
                provinceSet = new HashSet<Integer>();
            }
            provinceSet.remove(-1);
            provinceSet.add(temp.getProvinceId());
            apkConfig.setProvinceIds(provinceSet);
            apkConfigMap.put(apk, apkConfig);
        }
        return apkConfigMap;
    }

    /**
     * initReserveUserList:TODO 请描述该方法是做什么用途的
     *
     * @param apkConfig
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private void initReserveUserList(ApkPRIConfig apkConfig, int needSize) {
        String apk = apkConfig.getMark();
        // apk的省份信息
        Set<Integer> provinceSet = apkConfig.getProvinceIds();
        if (provinceSet == null || provinceSet.size() == 0) {
            LOGGER.warn("预安装 apk:{} base:{}  没有获取到省份", new Object[]{apk, apkConfig.getBase()});
            return;
        }
        provinceSet.remove(-1);
        Integer[] provinces = provinceSet.toArray(new Integer[0]);
        provinces = ProvinceUtil.randomProvinces(provinces);
        LOGGER.warn("预安装 apk:{} base:{}  共{}个省", new Object[]{apk, apkConfig.getBase(), provinces.length});
        Integer currDay = Integer.parseInt(DateUtil.getCurrentDateStr());
        String apkRecordKey = CacheKey.A_TASK + DateUtil.getDaysOfTime() + apk;
        int size = 0;
        List<AddReserveRecordVo> userList = new ArrayList<AddReserveRecordVo>();
        Object maxCacheLength = cacheParamManager.getParamValue(CacheKey.USER_CACHE_LENGTH);
        if (maxCacheLength != null) {
            MAXSIZE = Integer.parseInt(maxCacheLength.toString());
        }
        for (int i = 0; i < provinces.length; i++) {
            while (needSize > 0) {
                Integer province = provinces[i];
                ThirdUserQueryVo queryVo = new ThirdUserQueryVo();
                queryVo.setApkId(apkConfig.getId());
                queryVo.setBase(apkConfig.getBase());
                queryVo.setLength(MAXSIZE);
                queryVo.setProvinceId(province);
                queryVo.setStart(0);
                queryVo.setApk(apk);
                String carrierStr = apkConfig.getCarrier();
                if (StringUtils.isBlank(carrierStr)) {
                    queryVo.setCarrier(CarrierEnum.MOBILE.getType());
                } else {
                    String[] carriers = carrierStr.split(",");
                    int index = (int) (Math.random() * carriers.length);
                    queryVo.setCarrier(Integer.parseInt(carriers[index]));
                }
                ReserveUserVo user = selectUser(queryVo);
                if (user != null) {
                    // 是否做过
                    String imsi = user.getImsi();
                    Integer base = apkConfig.getBase();
                    Integer apkId = apkConfig.getId();
                    String hsman = user.getHsman();
                    String hstype = user.getHstype();
                    String imei = user.getImei();
                    int apkIdOrBase = getBaseOrApkId(base, apkId);
                    Set<Integer> hasDoApks = clientManager.getImsiHasDoApks(imsi);
                    if (!(hasDoApks.contains(apkIdOrBase)) && !clientManager.hasReserveDoApk(imsi, apkId, base)) {
                        // 插入record
                        AddReserveRecordVo record = new AddReserveRecordVo();
                        record.setImsi(imsi);
                        record.setProvinceId(province);
                        record.setRd(currDay);
                        record.setMark(apk);
                        record.setHsman(hsman);
                        record.setHstype(hstype);
                        record.setImei(imei);
                        userList.add(record);
                        clientManager.addReserveDoApk(imsi, apkId, base);
                        needSize--;
                        size++;
                    } else {
                        LOGGER.warn("预安装 apk:{} 省份{}  imsi:{}  已经做过", new Object[]{apk, province, imsi});
                    }
                } else {
                    LOGGER.warn("预安装 apk:{} 省份{}  没有获取到用户", new Object[]{apk, province});
                    break;
                }
            }
        }
        addReserveRecordDAO.batchInsertRecord(userList);
        redisClient.evalHincrby(apkRecordKey, "reserveexectotal", size, DateUtil.leftTimeToMidNight() / 1000);
    }

    /**
     * getBaseOrApkId:获取基地或者apkid
     *
     * @param base
     * @param apkId
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private int getBaseOrApkId(Integer base, Integer apkId) {
        int apkIdOrBase = 0;
        if (base == null || base == ApkBaseEnum.OTHER.getType()) {// 和基地无关
            apkIdOrBase = apkId;
        } else {// 和基地有关
            apkIdOrBase = base;
        }
        return apkIdOrBase;
    }

    private ReserveUserVo selectUser(ThirdUserQueryVo queryVo) {
        Integer start = queryVo.getStart();
        Integer provinceId = queryVo.getProvinceId();
        Integer base = queryVo.getBase();
        Integer apkId = queryVo.getApkId();
        int apkIdOrBase = getBaseOrApkId(base, apkId);
        Integer carrier = queryVo.getCarrier();
        String queueKey = CacheKey.RESERVE_USER_QUEUE + carrier + "_" + provinceId + "_" + apkIdOrBase;
        String userIndex = CacheKey.RESERVE_USER_INDEX + carrier + "_" + provinceId + "_" + apkIdOrBase;
        Object loginIndex = redisClient.get(userIndex);
        if (loginIndex != null) {
            start = Integer.parseInt(loginIndex.toString());
        }
        Object userObj = redisClient.pollFromQueue(queueKey);
        if (userObj != null) {
            LOGGER.warn("apk:{} base:{} 省份{} 获取到用户", new Object[]{apkId, base, provinceId});
            return (ReserveUserVo) userObj;
        } else {
            long startTime = System.currentTimeMillis();
            long expireTime = 10;
            Object timeObj = cacheParamManager.getParamValue(CacheKey.THIRD_REFRESH_TIME);
            if (timeObj != null) {
                expireTime = Integer.parseInt(timeObj.toString());
            }
            if (redisClient.evalSetNx(queueKey + "_LOCK", expireTime)) {
                queryVo.setStart(start);
                List<UserClientInfo> userList = selectUserList(queryVo);
                LOGGER.warn("apk:{} base:{} 省份{} 获取到的数据时间 {} 大小 {}", new Object[]{apkId, base, provinceId, System.currentTimeMillis() - startTime, userList.size()});
                if (userList != null && userList.size() > 0) {
                    for (int i = 0; i < userList.size(); i++) {
                        UserClientInfo user = userList.get(i);
                        ReserveUserVo userVo = new ReserveUserVo();
                        userVo.setImsi(user.getImsi());
                        userVo.setProvinceId(provinceId);
                        userVo.setHsman(user.getHsman());
                        userVo.setHstype(user.getHstype());
                        userVo.setImei(user.getImei());
                        redisClient.putToQueue(queueKey, userVo);
                        if (start < user.getId()) {
                            start = user.getId().intValue();
                        }
                    }
                    redisClient.put(userIndex, start);
                    return (ReserveUserVo) redisClient.pollFromQueue(queueKey);
                } else {
                    LOGGER.warn("apk:{} base:{} 省份{} 用户不够,当前下标:{}", new Object[]{apkId, base, provinceId, start});
                }
            }
        }
        return null;
    }

}
